/*
 *  ES2015 \u{H+} Unicode escape in string literals and identifier names.
 */

function safeEscape(v) {
    return Array.prototype.map.call(v, function (v) {
        if (v.charCodeAt(0) < 0x20 || v.charCodeAt(0) > 0x7f || v === '<' || v === '>') {
            return '<' + v.charCodeAt(0) + '>';
        }
        return v;
    }).join('');
}

/*===
string literal
"\u{}" -> SyntaxError
"\u{0}" -> 1 <0>
"\u{1}" -> 1 <1>
"\u{2}" -> 1 <2>
"\u{3}" -> 1 <3>
"\u{4}" -> 1 <4>
"\u{5}" -> 1 <5>
"\u{6}" -> 1 <6>
"\u{7}" -> 1 <7>
"\u{8}" -> 1 <8>
"\u{9}" -> 1 <9>
"\u{a}" -> 1 <10>
"\u{b}" -> 1 <11>
"\u{c}" -> 1 <12>
"\u{d}" -> 1 <13>
"\u{e}" -> 1 <14>
"\u{f}" -> 1 <15>
"\u{A}" -> 1 <10>
"\u{B}" -> 1 <11>
"\u{C}" -> 1 <12>
"\u{D}" -> 1 <13>
"\u{E}" -> 1 <14>
"\u{F}" -> 1 <15>
"\u{G}" -> SyntaxError
"\u{g}" -> SyntaxError
"\u{41}" -> 1 A
"\u{041}" -> 1 A
"\u{0041}" -> 1 A
"\u{00041}" -> 1 A
"\u{00000000000000000000000000000000000000000000000000000000000041}" -> 1 A
"\u{00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041}" -> 1 A
"\u{1234}" -> 1 <4660>
"\u{cafe}" -> 1 <51966>
"\u{ffff}" -> 1 <65535>
"\u{d801}" -> 1 <55297>
"\u{dc12}" -> 1 <56338>
"\u{10000}" -> 2 <55296><56320>
"\u{00010000}" -> 2 <55296><56320>
"\u{1f4a9}" -> 2 <55357><56489>
"\u{01f4a9}" -> 2 <55357><56489>
"\u{0000000000000000001f4a9}" -> 2 <55357><56489>
"\u{10ffff}" -> 2 <56319><57343>
"\u{010ffff}" -> 2 <56319><57343>
"\u{0010ffff}" -> 2 <56319><57343>
"\u{00000000000000000000010ffff}" -> 2 <56319><57343>
"\u{110000}" -> SyntaxError
"\u{000110000}" -> SyntaxError
"\u{12345678}" -> SyntaxError
"\u{123456789abcdef0}" -> SyntaxError
"\u{_10ffff}" -> SyntaxError
"\u{10ffff_}" -> SyntaxError
"\u{{10ffff}" -> SyntaxError
"\u{10ffff}}" -> 3 <56319><57343>}
===*/

function stringLiteralTest() {
    [
        // require at least one digit
        '',

        // single digit, allowed nybbles
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        'a', 'b', 'c', 'd', 'e', 'f',
        'A', 'B', 'C', 'D', 'E', 'F',

        // example of unallowed nybble
        'G', 'g',

        // various forms for escaping 'A', note that there's no limit for leading zero count
        '41',
        '041',
        '0041',
        '00041',
        '00000000000000000000000000000000000000000000000000000000000041',
        '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041',

        // some BMP characters, surrogate pairs
        '1234',
        'cafe',
        'ffff',
        'd801',
        'dc12',

        // non-BMP characters, decode into surrogate pairs
        '10000',
        '00010000',
        '1f4a9',
        '01f4a9',
        '0000000000000000001f4a9',
        '10ffff',
        '010ffff',
        '0010ffff',
        '00000000000000000000010ffff',

        // above BMP are rejected
        '110000',
        '000110000',
        '12345678',
        '123456789abcdef0',

        // some misc invalid forms
        '_10ffff',  // leading garbage
        '10ffff_',  // trailing garbage
        '{10ffff',  // leading extra curly
        '10ffff}',  // trailing extra curly -> actually parses in the eval string, result is <U+10ffff> followed by a '}'
    ].forEach(function (v) {
        var src = '"\\u{' + v + '}"';
        var res;
        try {
            res = eval(src);
            print(src, '->', res.length, safeEscape(res));
        } catch (e) {
            print(src, '->', e.name);
            //print(e.stack);
        }
    });
}

print('string literal');
stringLiteralTest();

/*===
plain escape
A -> 3 100
A+A -> 3 200
\u0041+\u0041 -> 3 200
\u{41}+\u{000000041} -> 3 200
\u0041\u002b\u0041 -> SyntaxError
\u{41}\u{02b}\u{000000041} -> SyntaxError
O.A + O.foo -> 4 A123
O.\u{41} + O.foo -> 4 A123
O.\u{41} + O.\u{66}\u{06f}\u{00000006f} -> 4 A123
O.\uD83D\uDCa9 -> SyntaxError
O.\u{1F4A9} -> SyntaxError
===*/

// Unicode escapes may also appear in source identifiers.

function plainEscapeTest() {
    var A = 100;
    var O = { foo: 123, A: 'A' };
    O['\ud83d\udca9'] = 'pile';

    [
        // A+A, identifiers can be escaped; operator cannot
        'A',
        'A+A',
        '\\u0041+\\u0041',
        '\\u{41}+\\u{000000041}',
        '\\u0041\\u002b\\u0041',  // error
        '\\u{41}\\u{02b}\\u{000000041}',  // error

        // property access
        'O.A + O.foo',
        'O.\\u{41} + O.foo',
        'O.\\u{41} + O.\\u{66}\\u{06f}\\u{00000006f}',

        // non-BMP characters also become surrogate pairs, here for property access
        'O.\\uD83D\\uDCa9',
        'O.\\u{1F4A9}',
    ].forEach(function (v) {
        var src = v;
        var res;
        try {
            res = String(eval(src));
            print(src, '->', res.length, safeEscape(res));
        } catch (e) {
            print(src, '->', e.name);
            //print(e.stack);
        }
    });
}

print('plain escape');
plainEscapeTest();
